DEV Community

javinpaul
javinpaul

Posted on • Edited on

Top 10 Object-Oriented Design Principles for writing Clean Code

Disclosure: This post includes affiliate links; I may receive compensation if you purchase products or services from the different links provided in this article.

The Object-Oriented Design Principles are the core of OOP programming, but I have seen most of the Java programmers chasing design patterns like Singleton pattern, Decorator pattern, or Observer pattern, and not putting enough attention on learning Object-oriented analysis and design.

It's important to learn the basics of Object-oriented programming like Abstraction, Encapsulation, Polymorphism, and Inheritance. But, at the same time, it's equally important to know object-oriented design principles.

They will help you to create a clean and modular design, which would be easy to test, debug, and maintain in the future.

I have regularly seen Java programmers and developers of various experience level, who have either never heard about these OOP and SOLID design principle, or simply doesn't know what benefits a particular design principle offers and how to apply these design principle in coding.

To do my part, I have jotted down all important object-oriented design principles and putting it here for quick reference. These will at least give you some idea about what they are and what benefit they offer.

I have not put examples, just to keep the article short but you can find a lot of examples of these design principles on the internet and even on my Java blog, just use the search bar at the top of the page.

If you are not able to understand a design principle, you should try to do more than one example because sometimes we connect to another example or author better but you must understand these design principles and learn how to use it in your code.

Another thing you can do is to join a comprehensive object-oriented design course like SOLID Principles of Object-Oriented Design by Steve Smith on Pluralsight. It has helped me a lot in my understanding and application of these principles.

Btw, I have also shared relevant and useful courses and books, both free and paid, and I will earn some money if you buy something which is not free. They are the resources I have used to learn SOLID design principles and Programming in general and quite useful for learning these principles in depth.

10 OOP & SOLID Design Principles for writing Clean Code

Though the best way of learning any design principle or pattern is a real-world example and understanding the consequences of violating that design principle, the subject of this article is Introducing Object-oriented design principles for Java Programmers, who are either not exposed to it or in the learning phase.

I personally think each of these OOP and SOLID design principles needs an article to explain them clearly, and I will definitely try to do that here, but for now, just get yourself ready for a quick bike ride on design principle town :)

1. DRY (Don't repeat yourself)

Our first object-oriented design principle is DRY, as the name suggests DRY (don't repeat yourself) means don't write duplicate code, instead use Abstraction to abstract common things in one place.

If you have a block of code in more than two places consider making it a separate method, or if you use a hard-coded value more than one time make them public final constant. The benefit of this Object-oriented design principle is in maintenance.

It's important not to abuse it, duplication is not for code, but for functionality.

It means if you have used common code to validate OrderId and SSN it doesn't mean they are the same or they will remain the same in the future.

By using common code for two different functionality or thing you closely couple them forever and when your OrderId changes its format, your SSN validation code will break.

So beware of such coupling and just don't combine anything which uses a similar code but is not related. You can further check out the Basics of Software Architecture & Design Patterns in Java course to learn more about writing good code and best practices to follow while designing a system.

You can also read the classical book Pragmatic Programmer by Dave Thomas and Andrew Hunt where it first coined this term and explains what exactly DRY means

2. Encapsulate What Changes

There is only one thing that is constant in the software field and that is "Change", So, encapsulate the code you expect or suspect to be changed in the future.

The benefit of this OOP Design principle is that It's easy to test and maintain proper encapsulated code.

If you are coding in Java then follow the principle of making variables and methods private by default and increasing access step by step like from a private to protected and not public.

Several of the design patterns in Java uses Encapsulation, the Factory design pattern is one example of Encapsulation which encapsulates object creation code and provides flexibility to introduce a new product later with no impact on existing code.

Btw, if you are interested in learning more about design patterns in Java and Object-Oriented Programming then you must check this Design Pattern Library course Pluralsight. It's one of the best collections of design patterns and advice on how to use them in the real world.

3. Open Closed Design Principle

According to this OOP design principle, "Classes, methods or functions should be Open for extension (new functionality) and Closed for modification".

This is another beautiful SOLID design principle, coined by Uncle Bob on his classic Clean Code book, which prevents someone from changing already tried and tested code.

The key benefit of this design principle is that already tried and tested code is not touched which means they won't break.

Here is a Java code example which violates the Open Closed Design Principle of Programming:

In this code GraphicEditor is tightly coupled with Shape, If you need a new Shape then you need to modify already tried and tested code inside the drawShape(Shape s) method, which is both error-prone and not desirable.

Ideally, if you are adding new functionality only then your code should be tested and that's the goal of Open Closed Design principle.

By the way, the Open-Closed principle is "O" from the SOLID acronym. If you want to learn more about this principle, the SOLID Principles of Object-Oriented Design and Architecture course on Udemy is one of the best resources to consult.

4. Single Responsibility Principle (SRP)

Single Responsibility Principle is another SOLID design principle and represents "S" on the SOLID acronym. As per SRP, there should not be more than one reason for a class to change, or a class should always handle single functionality.

The key benefit of this principle is that it reduces coupling between the individual component of the software and Code.

For example, If you put more than one functionality in one Class in Java it introduces coupling between two functionality and even if you change one functionality there is a chance you broke coupled functionality, which requires another round of testing to avoid any surprise on the production environment.

You can further see Design Pattern in Javacourse by Dmitri Nestruk on Udemy to learn about patterns which are based on this principle.

5. Dependency Inversion principle

Don't ask for dependency it will be provided to you by the framework. This has been very well implemented in Spring framework, one of the most popular Java framework for writing real-worth applications.

The beauty of this design principle is that any class that is injected by DI framework is easy to test with the mock object and easier to maintain because object creation code is centralized in the framework and client code is not littered with that.

There are multiple ways to implemented Dependency injection like using bytecode instrumentation which some AOP (Aspect Oriented Programming) framework like AspectJ does or by using proxies just like used in Spring.

It also represents "D" on the SOLID acronym.

Here is an example of the code which violates the Dependency Inversion Principle or DIP in Java:

You can see that AppManager depends upon EventLogWriter which is tightly coupled with the AppManager. If you need to use another way to notify your client like by sending push notifications, SMS, or E-mail, you need to change the AppManager class.

This problem can be solved by using the Dependency Inversion Principle where instead of AppManager asking for EventLogWriter, it will be injected or provided to AppManager by the framework.

You can further see Using SOLID Principles to Write Better Code --- A Crash Course on Udemy to learn more about the Dependency Inversion Principle and how to solve such problems.

6. Favor Composition over Inheritance

In OOP, There are two general ways to reuse the code you have already written, Inheritance and Composition, both have their own advantage and disadvantages, but, in general, you should always favor composition over inheritance, if possible.

Some of you may argue this, but I found that Composition is the lot more flexible than Inheritance.

Composition allows changing the behavior of a class at run-time by setting property during run-time and by using Interfaces to compose a class we use polymorphism which provides flexibility to replace with better implementation any time.

Even Joshua Bloch's Effective Java advise favoring composition over inheritance. If you are still not convinced then you can also read here to learn more about why your Composition is better than Inheritance for reusing code and functionality.

And, if you keep forgetting this rule, here is a nice cartoon to put on your desk :-)

If you are interested in learning more about Object-Oriented Programming Concepts like Composition, Inheritance, Association, Aggregation, etc, you can also take a look at the Object-Oriented Programming in Java course on Coursera.

It's free to explore and learn but you will be charged if you also want to participate in exercises, assignments, evaluations, and need Certification to show in your LinkedIn profile.

If you are joining this course to get Coursera certificate then you need to either enroll into the specialization or take a subscription plan like Coursera Plus which provides unlimited access to more than 5000+ Coursera courses, projects, and professional certificates.

This is not a design principle but rather than a best practice while coding in object oriented programming.

7. Liskov Substitution Principle (LSP)

According to the Liskov Substitution Principle, Subtypes must be substitutable for supertype I mean methods or functions which use superclass type must be able to work with the object of subclass without any issue".

LSP is closely related to the Single responsibility principle and Interface Segregation Principle.

If a class has more functionality then subclass might not support some of the functionality and does violate LSP.

In order to follow LSP SOLID design principle, derived class or subclass must enhance functionality, but not reduce them. LSP represents "L" on the SOLID acronym.

Here is a code example that violates the Liskov Substitution Principle in Java:

Liskov Substitution Principle in Java

The reason the LSP is violated is that the behavior of Square does not match that of Rectangle, like, calling setWidth changes the height as well, which is not the case for Rectangle.

One more example of LSP is java.sql.Date which violates the Liskov substitution principle, because even though java.sql.Date extends java.util.Date, you cannot pass around it to the method which expects java.util.Date because all time-related methods e.g. getHour(), getMinute() and getSeconds() method throws java.util.NotSupportedException.

If you are interested in a more real-world example, then the SOLID Principles of Object-Oriented Design course on Pluralsight is a good course to start with.

Btw, you would need a Pluralsight membership to get access to this course, which costs around $29 per month or $299 annually (14% discount). Even if you don't have a membership, you can still access this course for FREE by taking advantage of their 10-day free trial without any commitment, which is a great way to not just access this course for free but also to check the quality of courses before joining Pluralsight.

8. Interface Segregation Principle (ISP)

Interface Segregation Principle states that a client should not implement an interface if it doesn't use that.

This happens mostly when one interface contains more than one functionality, and the client only needs one functionality and no other.

There is no doubt that Interface design is a tricky job because once you release your interface you can not change it without breaking all implementation. Well, Java 8's default or defender method feature does provide a way for interface evolution but not all Programming language support those features.

Another benefit of this design principle in Java is, the interface has the disadvantage of implementing all methods before any class can use it so having single functionality means less method to implement.

If you don't get the benefit of the interface in coding then I suggest you read my blog post, the real usage of an interface in Java to learn more.

9. Programming for Interface not implementation

A programmer should always program for the interface and not for implementation this will lead to flexible code which can work with any new implementation of the interface.

In concrete words, you should use interface type on variables, return types of a method, or argument type of methods in Java like using SuperClass type to store object rather than using SubClass.

I mean

List numbers= getNumbers();

instead of

ArrayList numbers = getNumbers();

This has also been advised in many Java books including in Effective Java and Head First design pattern book.

Here is an example of Coding for the interface in Java:

If you are interested in improving the code quality of your program, I also suggest you take a look at the Refactoring to Design Patterns course on Udemy which will help you to improve the internal design with refactoring techniques and design patterns in C#.

10. Delegation principles

This is another useful design principle which also follows segregation of duties logic. It says, Don't do all stuff by yourself, delegate it to the respective class.

One of the classical example of delegation design principle is equals() and hashCode() methods in Java.

In order to compare two objects for equality, we ask the class itself to do a comparison instead of the Client class doing that check.

The key benefit of this design principle is no duplication of code and pretty easy to modify behavior. Event delegation is another example of this principle, where an event is delegated to handlers for handling.

Summary

All these object-oriented design principles help you write flexible and better code by striving for high cohesion and low coupling.

The theory is the first step, but what is most important is to develop the ability to find out when to apply these design principles.

Once you get hold of that, the next step is to learn Design patterns in Java, which uses these design patterns to solve common problems of application development and software engineering.

If you are looking for a nice course to start with, I suggest you join the From 0 to 1: Design Patterns --- 24 That Matter --- In Javacourse on Udemy. It's very comprehensive and you can get it for just $11 on their several flash sales.

Anyway, here is a nice summary of all these OOP design principles.

Find out, whether we are violating any design principle and compromising the flexibility of code, but again as nothing is perfect in this world, don't always try to solve the problem with design patterns and design principle they are mostly for large enterprise project which has longer maintenance cycle.

Bottom line is, professionals programmers should always strive for a highly cohesive and loosely couple solution, code, or design. Looking open source code from Apache and Google are some good ways of learning Java and OOP design principles.

They will show you, how design principles should be used in coding and Java programs. Java Development Kit follows many design principles like Factory Pattern in BorderFactory class, Singleton pattern in java.lang.Runtime class, Decorator pattern on various java.io classes.

If you are interested in learning object-oriented principles and patterns, then you can look at my another personal favorite Head First Object-Oriented Analysis and Design, an excellent book and probably the best material available in object-oriented analysis and design

Not many programmers know this book because it is often shadowed by its more popular cousin Head First Design Pattern by Eric Freeman, which is more about how these principles come together to create a pattern you can use directly to solve known problems.

These books help a lot to write better code, taking full advantage of various Object-oriented and SOLID design principles. Btw, if you really interested more in Java coding practices then read Effective Java 3rd Edition by Joshua Bloch, a gem by the guy who wrote Java Collection API.

If you want to learn more about SOLID design principles, here are some useful resources you can take a look at:

  1. Clean Code By [Robert Martin]
  2. SOLID Principles of Object-Oriented Design
  3. SOLID Principles of Object-Oriented Design and Architecture
  4. Refactoring by Martin Fowler

And, if you like this article, you may like these Java and Programming articles as well:
10 Things Java Programmer should learn
10 Books Every Programmer Must Read
10 Tips to Improve Your Programming skill
10 Tools Every Software Developer should know
5 Courses to Learn Software Architecture in Depth
20 Libraries and APIS Java Programmer Should Know
Top 10 Programming languages to Learn
10 Framework and Library Java and Web Developer Should Learn
10 of Best Python Courses to learn Coding

Thanks for reading this article. If you find these object-oriented design principles useful then please share them with your friends and colleagues. If you have any questions or feedback then please drop a note.

Btw, if you buy any of my recommended books or courses using links in this article, I'll also be get paid.

If you like this article then please consider following me (javinpaul). if you'd like to be notified of every new post and don't forget to followjavarevisited on Twitter!


P. S. --- If you are really passionate about your Coding and want to improve your coding skill there are no better books then Clean Code by Robert Martin and Refactoring by Martin P. Fowler. Just go and read them.

Top comments (20)

Collapse
 
mindplay profile image
Rasmus Schultz

Why are we still teaching new devs that DRY is #1 ?

DRY is an outdated and dogmatic principle - it really should be the #1 principle on the "most harmful" list.

Much has been written on this in the past year or so - most recently this:

kentcdodds.com/blog/aha-programming

This subject is so much more nuanced than simply "don't repeat", which leads every new developer directly into highly coupled code full of accidental dependencies.

We need to start teaching this principle more responsibly - which also means, we need to stop teaching this as the number one rule.

Collapse
 
javinpaul profile image
javinpaul

Hello @Rasmas, you may be right but for the most part "duplicate code" has been evil in coding, at least that's what I have learned. DRY is a reminder to address that duplication, but it's not just code but also the functionality. Having the same functionality at two places makes it hard to maintain.

Collapse
 
mindplay profile image
Rasmus Schultz

Having the same functionality at two places makes it hard to maintain.

That depends.

You can have similar (even identical) code in two different places for different reasons - for example, new devs often conflate input validation with domain validation, because these are often similar in terms of code, but they're done for very different reasons.

Having similar (or identical) code at two places, in this case, makes the codebase easier to maintain, because you can change one without affecting the other.

Real-world example: validation of an address form on the checkout page of an e-commerce page - versus validation of an address form on an admin-only page on the back-end of the same system.

Even if these input validations are 90% similar today, there's a very real risk of one needing to change independently of the other. If there's a complex aspect to these validations, say, validating a street address via an API, that concern can be factored to a common service - but the validation itself (in this case electing to use said service or not) is probably simple and easy enough to maintain, so there's no good reason to mix these validation concerns; eliminating similar (even identical) code in this case creates a liability.

Conflating unrelated responsibilities, even if it eliminates similar/identical code, can make a project harder to maintain.

DRY is harmful dogma that teaches new developers to view complexity as a function of the number of lines of code - that the objective is always to have as few lines of code as possible. Nowhere does it say, "unless that makes the software harder to maintain".

Duplication isn't duplication unless it's duplicating the same responsibility.

DRY is harmful that way, and I really think we need to teach a more nuanced approach.

Collapse
 
engrumair profile image
Umair

I understand that people sometimes overdo or apply things in the wrong places or without context but it does not mean that the said principle is wrong. It is understood wrong.
One can say, don't teache 'Classes' because then they will make too many classes. Or don't teach inheritance they will fall into the trap of class explosions.

Knowledge is different than competency. Knowledge is when one knows about the DRY (more than half of the developers)and competency is when to apply the DRY (Hardly anyone knows).

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
bennyflint profile image
Benjamin Flint • Edited

Good info. However, you are conflating dependency injection and dependency inversion (aka Inversion of Control). They are related, but definitely not the same thing. It would be worth it to clarify, as this is the SOLID principle that, in my experience, is the least followed (perhaps excepting interface segregation).

Collapse
 
melston profile image
Mark Elston

I saw this right away, as well. While other elements of the article are pretty good (or close enough) this one, IMHO, misses the mark entirely. Dependency Injection and Dependency Inversion are only tangentially related. Uncle Bob had a really good explanation of this principle (I think he coined the term).

I agree that this is probably the most overlooked (or misunderstood) principle in the list.

Collapse
 
mindplay profile image
Rasmus Schultz

Dependency injection is not only the last followed principle, it also should be the #1 principle - I've seen the code style and quality of an entire team improve substantially after teaching them DI, IOC and how DI containers work.

So many OO concepts fall into place once you really understand this :-)

Collapse
 
wangvnn profile image
wangvnn • Edited

Alan Kay once said that he did not foresee any things like these with OO. That does not mean DI, IOC are not good. Maybe we just need to call it something else like dependency driven design rather than calling its OO principles. OO is too far from Alan's original idea and land to a place where we deal with classes and their dependencies rather than objects themselves. I am just wondering.

Thread Thread
 
Sloan, the sloth mascot
Comment deleted
 
javinpaul profile image
javinpaul

Sorry, didn't know that Yuma is your dog :-) Well, yes, Head First Design Pattern can be best enjoyed when you are new to the subject matter, I first read it 14 years back when I didn't know anything about design patterns, composition, inheritance etc, except definitions. That book taught me how these concepts can help you write code which is flexible and easier to maintain.

Collapse
 
javinpaul profile image
javinpaul

You are correct, area() is a wrong choice because ultimately it will return the same value. The actual point is that you cannot pass Square object to methods expecting Rectangle and you are also correct in width and height behavior.

One more example of LSP is java.sql.Date which violates the Liskov substitution principle, because even though java.sql.Date extends java.util.Date, you cannot pass around it to the method which expects java.util.Date because all time-related methods e.g. getHour(), getMinute() and getSeconds() method throws java.util.NotSupportedException.

Collapse
 
joesta profile image
Joesta Sebolela

I have almost forgotten to use this principles (at least not completely). I should start being pragmatic and use them in many cases. Thanks a lot.

Collapse
 
javinpaul profile image
javinpaul

It's never too late to write clean code :-)

Collapse
 
w3bist profile image
Webist

You might be interested in M.O.M.

webist.eu/articles/software-develo...

Collapse
 
javinpaul profile image
javinpaul

Hello Yuma, it's definitely a classic, more like a reference book. Though, I enjoyed Head First Design Pattern more.

Collapse
 
klk profile image
Klk • Edited

It's obvious your heart's in the right place but you're examples aren't entirely correct; fix your explanation of dependency inversion & LSP as noted by other commentors.

Collapse
 
javinpaul profile image
javinpaul

Thanks @klk , I am taking notes.

Collapse
 
darkoverlordofdata profile image
Bruce Davidson

This is what, marketing cleverly embedded in stale advice? What kind of kickback are you getting on this courseware?

Some comments may only be visible to logged-in visitors. Sign in to view all comments.